/**
 * @author jcl_hqu
 * @date 2021/5/27 15:38
 */

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.text.csv.CsvData;
import cn.hutool.core.text.csv.CsvReader;
import cn.hutool.core.text.csv.CsvRow;
import cn.hutool.core.text.csv.CsvUtil;
import lombok.Data;

import java.util.*;
import java.util.concurrent.BlockingQueue;

/**
 * @description: TODO
 * @author ljc888898
 * @date 2021/5/27 15:38
 * @version 1.0
 */

@Data
public class Cpu {
    private List<Job> jobList; // 作业列表
    private boolean cpu_running=false;
    private Job current_job;
    private Dispatcher dispatcher=new Dispatcher();
    private Queue<Job> cpu_ready_queue=new LinkedList<>();

    private String getQueueIdList(Queue<Job> readyQueue) {
        StringBuilder readyQueue_id = new StringBuilder("");//结果存在字符串中

        // 迭代器访问结果
        Iterator<Job> iterator = readyQueue.iterator();
        while (iterator.hasNext()) {
            readyQueue_id.append(iterator.next().id + " ");
        }
        return readyQueue_id.toString();
    }

    private void calResult(Double time) {
        /**
         * @description: 计算到达时间、周转时间、等待时间
         * @param: time
         * @return:
         * @author jcl_hqu
         * @date: 2021/4/29 11:22
         */
        current_job.start_time = time;
        current_job.finish_time = time + current_job.cpu_burst;
        current_job.wait_time = time - current_job.getArrival_time();
        current_job.turnaround_time = current_job.finish_time - current_job.arrival_time;
        current_job.weighted_turnaround_time = current_job.turnaround_time / current_job.cpu_burst;
    }

    private double[] calAve(List<Job> result_list) {
        /**
         * @description: 计算平均周转时间、平均等待时间
         * @param: result_list
         * @return:
         * @author jcl_hqu
         * @date: 2021/4/29 11:20
         */
        double AverageTAT = 0;
        double AverageWTAT = 0;
        double AverageWT = 0;
        double num = result_list.size();
        for (Job p : result_list) {
            AverageTAT += p.getTurnaround_time();
            AverageWTAT += p.getWeighted_turnaround_time();
            AverageWT += p.getWait_time();
        }

        AverageTAT /= num;
        AverageWTAT /= num;
        AverageWT /= num;
        return new double[]{AverageTAT, AverageWTAT, AverageWT};
    }

    public void printResult(List<Job> list) {
        /**
         * @description:
         *   打印运行结果
         */

        System.out.println();
        System.out.format("%16s  %16s  %16s  %16s \n", "Process Num.", " Turnaround Time", "WeightedTurnaround Time", "Waiting Time");
        for (Job vp : list) {
            System.out.format("%14d     %14.2f     %14.2f     %14.2f \n", vp.id, vp.turnaround_time, vp.weighted_turnaround_time, vp.wait_time);
        }
    }

    // 将一批作业载入CPU中
    public void load_Job(String path){
        CsvReader reader = CsvUtil.getReader();
        CsvData data = reader.read(FileUtil.file(System.getProperty("user.dir") + path)); //路径
        List<CsvRow> rows = data.getRows();
        List<Job> Jobs = new ArrayList<>();
        boolean flag = false;//不写第一行到JobList中
        for (CsvRow csvRow : rows) {
            if (!flag) {
                flag = true;
                continue;
            }
            Long id = Long.valueOf(csvRow.getRawList().get(0));
            Long arrival_time = Long.valueOf(csvRow.getRawList().get(1));
            Long service_time = Long.valueOf(csvRow.getRawList().get(2));
            Jobs.add(new Job(id, arrival_time, service_time));
        }
        this.jobList=new ArrayList<>(Jobs);
    }

    // Cpu 执行工作
    public double[] cpu_working(){
        /** 
         * @description: Cpu使用HRRN调度算法运行一批作业
         * @param:  
         * @return:  平均时间
         * @author jcl_hqu
         * @date: 2021/5/29 19:54
         */ 
        List<Job> result_list = new LinkedList<>();  // 结果链表
        System.out.println("------HRRN Running-----");
        List<Job> current_jobs=new LinkedList<>(); // 当前作业数量
        double current_time=0;
        int Jobs_num=this.jobList.size();

        System.out.println("Process Num.    Start Time   End Time   Ready Queue");
        while(true){
            for (Job job:jobList) {
                if (job.getArrival_time()==current_time&&!current_jobs.contains(job)){
                    current_jobs.add(job); // 将新到达的作业添加到当前作业中
                }
            }
            if (Jobs_num==0) break; // 所有作业完成，结束循环

            if (cpu_running==false) {
                if (current_jobs.size()==0) continue; // 当前没有作业
                Scheduler scheduler = new Scheduler(current_jobs); // 初始化排队器
                Queue<Job> ready_queue = new LinkedList<>(scheduler.HRRN(current_time)); // 排队并返回结果
                this.current_job=dispatcher.getJob(ready_queue); //出队
                calResult(current_time);// 计算结果
                cpu_ready_queue = dispatcher.getReady_queue(); //修改CPU就绪队列
                result_list.add(current_job); // 添加到结果列表
                cpu_running=true; //标识现在CPU正在工作
                current_time+=1; // 当前时刻+1
            }else {
                current_time += 1; // 当前时刻+1
                if ((current_time - 1) == current_job.getFinish_time()) { //如果到达结束状态
                    current_time -= 1;  //回退
                    cpu_running = false; // 结束
                    current_jobs.remove(current_job); // 移除当前作业
                    Jobs_num--; // 作业数减少
                    String readyQueue_id = getQueueIdList(cpu_ready_queue); // 获得就绪队列中情况

                    // 输出结果
                    System.out.format("%8d     %8.2f     %8.2f     %8s \n", current_job.id, current_job.start_time, current_job.finish_time, readyQueue_id.toString());
                }
            }
        }
        printResult(result_list); // 打印结果
        return calAve(result_list); // 返回平均时间
    }

    public double[] cpu_working_RR() {
        /** 
         * @description: 模拟RR调度算法 
         * @param:  
         * @return:  
         * @author jcl_hqu
         * @date: 2021/5/29 20:14
         */ 
        int RR_count = 0; //时间片运行累积
        int RR_time_slice = 6; // 时间片大小
        List<Job> result_list = new LinkedList<>(); // 结果列表
        System.out.println("------RR Running-----");
        double current_time = 0;
        int Jobs_num = this.jobList.size();

        for (int i = 0; i < jobList.size(); i++) {
            jobList.get(i).remaining_service_time = jobList.get(i).cpu_burst;
        }

        System.out.println("Process Num.    Start Time   End Time   Ready Queue");
        while (true) {
            for (Job job : jobList) {
                if (job.remaining_service_time == job.cpu_burst && job.getArrival_time() == current_time && !cpu_ready_queue.contains(job)) {
                    cpu_ready_queue.offer(job);
                } // 将新到达的且没有执行过的作业添加到就绪队列
            }
            if (Jobs_num == 0) break;

            // 未在执行作业
            if (cpu_running == false) {
                if (cpu_ready_queue.size() == 0) continue;
                current_job = cpu_ready_queue.poll();  // 出队
                cpu_running = true;
                if (current_job.remaining_service_time == current_job.cpu_burst) {
                    this.current_job.start_time = current_time;
                }
                RR_count = 0;
                current_time += 1;
                RR_count += 1; //时间片计数+1
                current_job.remaining_service_time -= 1;
            } else {
                // 时间片到该调度或服务时间为0提前终止
                if (current_job.remaining_service_time == 0 || RR_count == RR_time_slice) {
                    cpu_running = false;

                    if (current_job.remaining_service_time == 0) {
                        // 到达时间、等待时间修改，并添加到作业完成列表
                        current_job.finish_time = current_time;
                        current_job.turnaround_time = current_job.finish_time - current_job.arrival_time;
                        current_job.weighted_turnaround_time = current_job.turnaround_time / current_job.cpu_burst;
                        current_job.wait_time=current_job.turnaround_time-current_job.cpu_burst;
                        result_list.add(current_job);
                        cpu_ready_queue.remove(current_job);
                        Jobs_num--;
                        String readyQueue_id = getQueueIdList(cpu_ready_queue);
                        System.out.format("%8d     %8.2f     %8.2f     %8s \n", current_job.id, current_job.start_time, current_job.finish_time, readyQueue_id.toString());
                    } else {
                        // 时间片结束，作业添加到就绪队列末尾
                        cpu_ready_queue.offer(current_job);
                        jobList.remove(current_job);  //修改joblist时间
                        jobList.add(current_job);
                    }
                } else {
                    current_job.remaining_service_time -= 1;
                    RR_count += 1;
                    current_time += 1;
                }
            }
        }
            printResult(result_list);
            return calAve(result_list);
    }
    public static void main(String[] args) {
        Cpu cpu=new Cpu();
        cpu.load_Job("./EX4/res/testData.csv");
        double[] HRRN = cpu.cpu_working();
        cpu.load_Job("./EX4/res/testData.csv");
        double[] RR = cpu.cpu_working_RR();

        //输出平均结果
        System.out.println("Algorithm    Average TAT   Average WTAT   Average WT");
        System.out.format("%8s    %8.2f     %8.2f     %8.2f      \n", "RR", RR[0], RR[1], RR[2]);
        System.out.format("%8s    %8.2f     %8.2f     %8.2f      \n", "HRRN", HRRN[0], HRRN[1], HRRN[2]);
    }
}
